package study.translation.parser;

import study.translation.grammar.Nonterminal;
import study.translation.grammar.Symbol;
import study.translation.grammar.Terminal;

import java.util.ArrayList;
import java.util.Iterator;

public class ParseTree
{
    public interface Node
    {
        public Symbol getSymbol();
    }

    public static class InnerNode implements Node
    {
        private Nonterminal nonterminal;

        private NodeList childNodes;

        public InnerNode( Nonterminal nonterminal )
        {
            this( nonterminal, new NodeList() );
        }

        public InnerNode( Nonterminal nonterminal, NodeList childNodes )
        {
            this.nonterminal = nonterminal;
            this.childNodes = childNodes;
        }

        public Symbol getSymbol()
        {
            return nonterminal;
        }

        public void addChildNode( Node node )
        {
            childNodes.add( node );
        }

        public NodeList getChildNodes()
        {
            return childNodes;
        }

        public Node getChildNode( int index )
        {
            return childNodes.get( index );
        }

        public boolean hasChildNodes()
        {
            return !childNodes.isEmpty();
        }

        public String toString()
        {
            return nonterminal.toString();
        }
    }

    public static class LeafNode implements Node
    {
        private Terminal terminal;

        private Lexem lexem;

        public LeafNode( Terminal terminal, Lexem lexem )
        {
            this.terminal = terminal;
            this.lexem = lexem;
        }

        public Symbol getSymbol()
        {
            return terminal;
        }

        public Lexem getLexem()
        {
            return lexem;
        }

        public int getLineNumber()
        {
            return lexem.getBegin().getLine();
        }

        public int getCharNumber()
        {
            return lexem.getBegin().getCharacter();
        }

        public String toString()
        {
            return terminal.toString();
        }
    }

    public static class NodeList extends ArrayList<Node>
    {
        public static class NodeListIterator implements Iterator<Node>
        {
            private Iterator<Node> i;

            public NodeListIterator( Iterator<Node> i )
            {
                this.i = i;
            }

            public boolean hasNext()
            {
                return i.hasNext();
            }

            public Node next()
            {
                return i.next();
            }

            public InnerNode nextInnerNode()
            {
                return ( InnerNode )next();
            }

            public LeafNode nextLeaf()
            {
                return ( LeafNode )next();
            }

            public void remove()
            {
                i.remove();
            }
        }

        public NodeList()
        {
        }

        public NodeList( int initialCapacity )
        {
            super( initialCapacity );
        }

        public NodeListIterator iterator()
        {
            return new NodeListIterator( super.iterator() );
        }
    }

    private InnerNode root;

    public ParseTree( InnerNode root )
    {
        this.root = root;
    }

    public InnerNode getRoot()
    {
        return root;
    }
}
